package com.redsoft.spirit.service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.redsoft.spirit.dao.AccountRepository;
import com.redsoft.spirit.dao.AuthorizationRepository;
import com.redsoft.spirit.dao.ComCodeTableRepository;
import com.redsoft.spirit.dao.DeptRepository;
import com.redsoft.spirit.entity.Authorization;
import com.redsoft.spirit.entity.ComCodeTable;
import com.redsoft.spirit.entity.Department;
import com.redsoft.spirit.entity.Role;
import com.redsoft.spirit.entity.TreeNode;

/**
 * 操作授权的service
 * 
 * <pre>
 * 操作授权的service
 * </pre>
 * 
 * @author 杨雷
 * @date 2017.06.16
 * @since 1.0
 */
@Service
@Transactional
public class AuthorizationService {

	@Autowired
	private AuthorizationRepository authorizationRepository;

	@Autowired
	private ComCodeTableService codeTableService;

	@Autowired
	private RoleService roleService;

	@Autowired
	private ComCodeTableRepository codeTableRepository;

	@Autowired
	private DeptRepository deptRepository;

	@Autowired
	private AccountRepository accountRepository;

	@Autowired
	private AuthorizationService authorizationService;

	/**
	 * 把模块和模块下的页面做成树
	 * 
	 * <pre>
	 * 把模块和模块下的页面做成树
	 * </pre>
	 * 
	 * @author 杨雷
	 * @return
	 */
	public List<TreeNode> getTreeData() {
		List<TreeNode> tree = new ArrayList<>();

		// 使用sql查询出码表中的配置为moudle的子项以及对应page表中的数据
		List<ComCodeTable> mouldes = codeTableService.getByParentInnerName("SYS_MOUDLE");

		for (ComCodeTable object : mouldes) {
			TreeNode node = new TreeNode();
			node.setId(object.getId());
			node.setLabel(object.getItemName());
			tree.add(node);
		}
		return tree;
	}

	/**
	 * 根据这个页面查询当前传过来的角色已经拥有的元素.
	 * 
	 * <pre>
	 * 根据这个页面查询当前传过来的角色已经拥有的元素
	 * </pre>
	 * 
	 * @author 杨雷
	 * @param roleList
	 * @param pageId
	 * @return
	 */
	public List getAuthorizations(List<Role> roleList, String pageId) {
		List<String> list = new ArrayList<String>();
		for (Role role : roleList) {
			list.add(role.getId());
		}

		String sql = "SELECT * FROM sys_authorization a WHERE a.page_id = :pageId AND a.auth_id IN (:roleId)";
		Map map = new HashMap<>();
		map.put("pageId", pageId);
		map.put("roleId", list);

		return authorizationRepository.sqlQuery(sql, map);
	}

	/**
	 * 更新操作授权的方法
	 * 
	 * <pre>
	 * 根据authId和元素id从数据查询是否含有该数据，如果有，则删除，如果没有，则添加
	 * </pre>
	 * 
	 * @author 杨雷
	 * @param authId
	 * @param pageInnerName
	 * @param pageId
	 * @param elementId
	 */
	@CacheEvict(value = { "user-auth-cache" }, allEntries = true, beforeInvocation = true)
	public void update(String authId, String elementInnerName, String pageId, String pageInnerName) {
		Authorization auth = authorizationRepository.findByAuthIdAndElementInnerNameAndPageId(authId, elementInnerName,
				pageId);

		if (auth == null) {
			auth = new Authorization();
			auth.setPageId(pageId);
			auth.setPageInnerName(pageInnerName);
			auth.setElementInnerName(elementInnerName);
			auth.setAuthId(authId);

			authorizationRepository.save(auth);
			return;
		}
		authorizationRepository.delete(auth);
	}

	/**
	 * 根据用户id查询这个用户拥有的所有的操作授权，加入缓存中
	 * 
	 * <pre>
	 * 	根据用户id查询这个用户拥有的所有的操作授权，加入缓存中,先查询这个人所有的角色，然后根据角色查找这个人所有的操作授权
	 * </pre>
	 * 
	 * @author 杨雷
	 * @param userId
	 * @return
	 */
	@Cacheable(value = "user-auth-cache", key = "'user-auth-cache-key-' + #userId")
	public List getAuthorizationsByUserId(String userId) {
		List<Role> roles = roleService.getRoleListByUserId(userId);

		List<String> lst = new ArrayList<String>();
		for (Role role : roles) {
			lst.add(role.getId());
		}
		String sql = "SELECT * FROM sys_authorization a WHERE a.auth_id IN (:roleId)";
		Map map = new HashMap<>();
		map.put("roleId", lst);
		return authorizationRepository.sqlQuery(sql, map);
	}

	/**
	 * 根据角色选取所有拥有的数据权限选项的集合.
	 * 
	 * <pre>
	 * 	根据角色选取所有拥有的数据权限选项的集合，去重复
	 * </pre>
	 * 
	 * @author 杨雷
	 * @param roleList
	 * @return
	 */
	public List getAuthorizationDataOptions(List<Role> roleList) {
		List lst = new ArrayList<>();
		for (Role role : roleList) {
			lst.add(role.getId());
		}
		String sql = "select * from com_code_table c where c.inner_name in(SELECT a.element_inner_name FROM sys_authorization a WHERE a.auth_id IN (:roleId) and a.auth_type='data')";
		Map map = new HashMap<>();
		map.put("roleId", lst);
		return codeTableRepository.sqlQuery(sql, map);
	}

	/**
	 * 根据角色list查询这些角色拥有的所有数据授权.
	 * 
	 * <pre>
	 * 根据角色list查询这些角色拥有的所有数据授权
	 * </pre>
	 * 
	 * @author 杨雷
	 * @param roleList
	 * @return
	 */
	public List getAuthorizationData(List<Role> roleList) {
		List lst = new ArrayList<>();
		for (Role role : roleList) {
			lst.add(role.getId());
		}
		String sql = "SELECT * FROM sys_authorization a WHERE a.auth_id IN (:roleId) and a.auth_type='data'";
		Map map = new HashMap<>();
		map.put("roleId", lst);

		return authorizationRepository.sqlQuery(sql, map);
	}

	/**
	 * 更新这个角色的数据授权规则.
	 * 
	 * <pre>
	 * 更新这个角色的数据授权规则，并清除缓存
	 * </pre>
	 * 
	 * @author 杨雷
	 * @param authId
	 * @param elementInnerName
	 */
	@CacheEvict(value = { "user-auth-cache" }, allEntries = true, beforeInvocation = true)
	public void updateAuthorizationData(String authId, String elementInnerName) {
		Authorization auth = authorizationRepository.findByAuthIdAndAuthType(authId, "data");

		// 如果没有则保存到数据库里
		if (auth == null) {
			auth = new Authorization();

			auth.setElementInnerName(elementInnerName);
			auth.setAuthId(authId);
			auth.setAuthType("data");

			authorizationRepository.save(auth);
			return;
		}

		// 如果已经有了，则更新这个通用权限值
		auth.setElementInnerName(elementInnerName);
		authorizationRepository.save(auth);
	}

	/**
	 * 根据这个人的userId查询这个人的所有角色，以及所有角色对应拥有的数据权限，按照order顺序，取最小值的表示优先级最大.
	 * 
	 * <pre>
	 * 根据这个人的userId查询这个人的所有角色，以及所有角色对应拥有的数据权限，按照order顺序，取最小值的表示优先级最大，即拥有最高权限，整个组织机构的
	 * </pre>
	 * 
	 * @author 杨雷
	 * @param userId
	 * @param sqlOrg
	 * @param countSql
	 * @param map
	 * @return
	 */
	public void getAuthSql(String userId, StringBuilder sqlOrg, StringBuilder countSql, Map map) {
		List<ComCodeTable> lst = authorizationService.getAuthCodeTable(userId);
		if (lst == null || lst.size() == 0) {
			return;
		}
		ComCodeTable codeTable = lst.get(0);

		// 如果权限是查询本部门及所有下级部门，则需要取出这个人的所有部门，然后遍历所有部门的下级的所有人（考虑到一个人多部门存在，才使用hardcode）
		if (codeTable.getInnerName().equals("DATA-AUTH-DEPT-ALL")) {

			// 为啥用set,就是因为要给重复部门的人员给去重复一下
			Set<String> set = authorizationService.getAuthDeptAllUserIds(userId);

			// 根据修改引用来修改最初的sql地址内容
			sqlOrg.append(" and creator_id in(:authUserList)").toString();
			countSql.append(" and creator_id in(:authUserList)").toString();
			List empListAll = new ArrayList<>();
			empListAll.addAll(set);
			map.put("authUserList", empListAll);
		} else if (codeTable.getInnerName().equals("DATA-AUTH-DEPT")) {
			sqlOrg.append(" and creator_id in(:authUserList)").toString();
			countSql.append(" and creator_id in(:authUserList)").toString();
			List empList = authorizationService.getAuthDeptUserIds(codeTable.getItemValue(), userId);

			map.put("authUserList", empList);
		} else if (codeTable.getInnerName().equals("DATA-AUTH-USER")) {
			sqlOrg.append(" and creator_id =:authUserId").toString();
			countSql.append(" and creator_id =:authUserId").toString();
			map.put("authUserId", userId);
		}
	}

	/**
	 * 根据这个人查询他对应的角色数据授权的最高级规则.
	 * 
	 * <pre>
	 * 根据这个人查询他对应的角色数据授权的最高级规则，并存储在缓存里
	 * </pre>
	 * 
	 * @author 杨雷
	 * @param userId
	 * @return
	 */
	@Cacheable(value = "user-auth-cache", key = "'user-auth-data-codetable-key-' + #userId")
	public List<ComCodeTable> getAuthCodeTable(String userId) {
		String sql = "select c.* from sys_authorization a, com_code_table c where auth_id in "
				+ " (select role_id from sys_role_account ra where ra.ACCOUNT_ID= ?) "
				+ " and auth_type = 'data' and a.element_inner_name = c.inner_name order by c.display_order asc";

		return codeTableRepository.sqlQuery(sql, userId);
	}

	/**
	 * 根据这个人查询这个人的所在部门以及所在部门的所有下级的所有人.
	 * 
	 * <pre>
	 * 根据这个人查询这个人的所在部门以及所在部门的所有下级的所有人，去重复，存储在缓存里
	 * </pre>
	 * 
	 * @author 杨雷
	 * @param userId
	 * @return
	 */
	@Cacheable(value = "user-auth-cache", key = "'user-auth-data-ALL-userIds-key-' + #userId")
	public Set<String> getAuthDeptAllUserIds(String userId) {
		List<Department> depts = deptRepository.getDeptsByUserId(userId);

		// 为啥用set,就是因为要给重复部门的人员给去重复一下
		Set<String> set = new HashSet<>();
		for (Department dept : depts) {
			String userDeptSql = "call p_dept_emp_path(?)";
			List empList = accountRepository.sqlQueryNotFormat(userDeptSql, dept.getId());
			set.addAll(empList);
		}
		return set;
	}

	/**
	 * 根据这个人，查询这个人所在部门的所有人员的id.
	 * 
	 * <pre>
	 * 根据这个人，查询这个人所在部门的所有人员的id，sql是从码表里读取出来的，存储在缓存里
	 * </pre>
	 * 
	 * @author 杨雷
	 * @param sql
	 * @param userId
	 * @return
	 */
	@Cacheable(value = "user-auth-cache", key = "'user-auth-data-DEPT-userIds-key-' + #userId")
	public List getAuthDeptUserIds(String sql, String userId) {
		return accountRepository.sqlQueryNotFormat(sql, userId);
	}
}
